package com.storm.common.jackson

import cn.hutool.core.util.StrUtil
import com.fasterxml.jackson.annotation.JsonValue
import com.fasterxml.jackson.core.JsonParseException
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.util.EnumResolver
import java.io.IOException
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.util.*

class CustomizeEnumDeserializer(private val enumResolver: EnumResolver) : JsonDeserializer<Enum<*>?>() {
    @Throws(IOException::class, JsonProcessingException::class)
    override fun deserialize(p: JsonParser, context: DeserializationContext): Enum<*>? {
        val enumClass = enumResolver.enumClass
        val rawEnums = enumResolver.rawEnums
        //获取@JsonValue的方法
        var first = enumClass.methods.first { method: Method ->
            method.isAnnotationPresent(JsonValue::class.java)
                    || method.returnType == Int::class.java
        }
        if (first==null) {
            val field = dealEnumType(enumClass).orElseThrow {
                JsonParseException(
                    p,
                    String.format("Could not find @JsonValue in Class: %s.", enumClass.name)
                )
            }
            first = try {
                enumClass.getDeclaredMethod(getMethodCapitalize(field, field.name))
            } catch (e: NoSuchMethodException) {
                throw JsonParseException(p, "@JsonValue字段需要定义get/is方法")
            }
        }
        for (en in rawEnums) {
            val anEnum: Enum<*>? = findEnum(p, first, en)
            if (Objects.nonNull(anEnum)) return anEnum
        }
        return null
    }

    private fun findEnum(p: JsonParser, method: Method, en: Enum<*>): Enum<*>? {
        return try {
            val rt = method.returnType
            if (Int::class.java.isAssignableFrom(rt)) {
                val value = method.invoke(en) as Int
                if (value == p.valueAsInt) {
                    if ("" != p.valueAsString) {
                        return en
                    }
                }
            } else {
                val value = p.valueAsString
                if (method.invoke(en) == value) {
                    return en
                }
            }
            val value = p.valueAsString
            if (en.toString() == value) {
                en
            } else null
        } catch (e: IllegalAccessException) {
            null
        } catch (e: InvocationTargetException) {
            null
        } catch (e: IOException) {
            null
        }
    }

    companion object {
        /**
         * 获取有@JsonValue注解的字段
         * @param clazz 类
         * @return 注解的字段或空 Optional
         */
        fun dealEnumType(clazz: Class<*>): Optional<Field> {
            return if (clazz.isEnum) Arrays.stream(clazz.declaredFields).filter { field: Field -> field.isAnnotationPresent(JsonValue::class.java) }.findFirst() else Optional.empty()
        }

        fun getMethodCapitalize(field: Field, str: String?): String {
            val fieldType = field.type
            return (if (Boolean::class.javaPrimitiveType == fieldType) "is" else "get") + StrUtil.upperFirst(str)
        }
    }

}